#include <avr/io.h>             // this contains all the IO port definitions
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#define TIMER1_PRESCALE_1 1
#define TIMER1_PRESCALE_8 2
#define TIMER1_PRESCALE_64 3
#define TIMER1_PRESCALE_256 4
#define TIMER1_PRESCALE_1024 5
#define SENSOR 3

#define HEX__(n) 0x##n##UL

#define B8__(x) ((x&0x0000000FLU)?1:0)  \
               +((x&0x000000F0LU)?2:0)  \
               +((x&0x00000F00LU)?4:0)  \
               +((x&0x0000F000LU)?8:0)  \
               +((x&0x000F0000LU)?16:0) \
               +((x&0x00F00000LU)?32:0) \
               +((x&0x0F000000LU)?64:0) \
               +((x&0xF0000000LU)?128:0)

#define B8(d) ((unsigned char)B8__(HEX__(d)))

void ioinit (void);

// used to count how many blinks are done in a single rotation
uint8_t blink_count = 0;

// blinkrate, starts out at 10000
uint16_t blinkrate = 10000;

// store all the image data in program memory (ROM)
// instead of RAM (the default)
const uint8_t large_image[] PROGMEM = { B8 (00011100),
B8(01111110),
B8(11000011),
B8(11000011),
B8(11000011),
B8(00000000),
B8(11111111),
B8(00011000),
B8(00011000),
B8(00011000),
B8(11111111),
B8(00000000),
B8(11111100),
B8(01100011),
B8(01100011),
B8(01100011),
B8(11111100),
B8(00000000),
B8(11111111),
B8(11000011),
B8(11000011),
B8(11000011),
B8(01111110),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(10000010),
B8(01000100),
B8(00101000),
B8(00010000),
B8(00010000),
B8(00010000),
B8(00010000),
B8(00010000),
B8(11111110),
B8(01111100),
B8(00111000),
B8(00010000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),
B8(00000000),

};

// special pointer for reading from ROM memory
PGM_P largeimage_p PROGMEM = large_image;

#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))
int imagesize = NUM_ELEM (large_image);


// this function is called when timer1 compare matches OCR1A
uint8_t j = 0;
SIGNAL (SIG_TIMER1_COMPA)
{
  if (j >= imagesize)
    j = 0;

  // read the image data from ROM
  PORTB = pgm_read_byte (largeimage_p + j);
  j++;

  // add to the blink count.  we max at 200 so we don't get an overflow.  
  if (blink_count < 200) {
     blink_count++;
  }
}

int
main (void)
{

  DDRB = 0xFF;                  // set all 8 pins on port B to outputs
  DDRD = 0xFB;                  // one input on pin D2, this pin read the hall sensor
  PORTD = 0x04;                 // turn on pullup on pin D2

  ioinit ();
  sei ();                       // Set Enable Interrupts
  while (1)
    {
    }
}

SIGNAL (SIG_INT0)
{
  // Start the image back a pixel 1 when we see the sensor
  j = 0;
  TCNT0 = 0;
 
  // if blink_count is less than imagesize then the blinks are lasting too long and need to be shortened
  // I only change the rate in increments of 100.  This means it my take a few revolutions to finally
  // get the right rate.  Sometimes the sensor misses the magnet and in those cases I don't wont the rate to change drastically
  // I probably need to adjust where the sensor and magnet or posistioned. 
  if (blink_count < imagesize )
    {
          // I made an arbitrary minimum rate
          if ( blinkrate > 4000) {
             blinkrate = blinkrate - 100;
          }
    }  else {
          // I made an arbitrary maximum rate
          if ( blinkrate < 40000) {
             blinkrate = blinkrate + 100;
          }
    }
  TCCR1B = (1 << WGM12) | TIMER1_PRESCALE_1;
  OCR1A = blinkrate; 
  TIMSK |= 1 << OCIE1A;
  blink_count = 0;
  sei ();                       // Set Enable Interrupts
}

void ioinit (void)
{
  // interrupt on INT1 pin falling edge (sensor triggered) and
  PCMSK |= (1 << PIND2);
  // turn on interrupts!  Set the interrupt to only trigger when the sensor goes back low
  MCUCR = (1<<ISC01) | (1<<ISC00);
  GIMSK |= (1 << INT0);

  TCCR1B = (1 << WGM12) | TIMER1_PRESCALE_1;
  OCR1A = blinkrate;
  TIMSK |= 1 << OCIE1A;
}

